Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize the performance of InternalConfigurationRootExtensions.GetChildrenImplementation #74736

Closed
wants to merge 24 commits into from

Conversation

wangyuantao
Copy link

@wangyuantao wangyuantao commented Aug 29, 2022

@rjgotten I implemented your design at #65885

The benchmark of dotnet/performance#2572 shows (-c Release -f net7.0)

Before

Method ConfigurationProvidersCount KeysCountPerProvider Mean Error StdDev Median Min Max Gen 0 Gen 1 Allocated
Get 8 10 596.7 us 3.02 us 2.68 us 595.3 us 593.8 us 601.5 us 93.7500 - 399.91 KB
Get 8 20 1,356.1 us 7.20 us 6.39 us 1,356.2 us 1,347.0 us 1,370.6 us 204.5455 - 870.41 KB
Get 8 40 3,093.9 us 27.59 us 23.04 us 3,090.9 us 3,063.6 us 3,142.3 us 455.6962 25.3165 1955.57 KB
Get 16 10 2,369.3 us 50.63 us 58.31 us 2,344.8 us 2,297.9 us 2,486.4 us 343.7500 - 1467.93 KB
Get 16 20 5,528.9 us 108.01 us 101.03 us 5,511.9 us 5,422.6 us 5,740.6 us 777.7778 66.6667 3278.57 KB
Get 16 40 12,783.1 us 254.98 us 238.51 us 12,826.2 us 12,416.2 us 13,207.8 us 1750.0000 62.5000 7446.58 KB
Get 32 10 10,895.5 us 82.95 us 64.76 us 10,892.3 us 10,814.7 us 11,028.9 us 1590.9091 136.3636 6715.57 KB
Get 32 20 24,598.7 us 841.96 us 935.83 us 24,001.3 us 23,732.4 us 26,550.0 us 3375.0000 - 14451.19 KB
Get 32 40 53,304.3 us 402.64 us 356.93 us 53,276.8 us 52,694.5 us 54,120.3 us 7500.0000 250.0000 32475.59 KB

After

Method ConfigurationProvidersCount KeysCountPerProvider Mean Error StdDev Median Min Max Gen 0 Gen 1 Allocated
Get 8 10 133.7 us 2.49 us 2.08 us 133.5 us 131.2 us 138.3 us 11.0294 0.5252 46.66 KB
Get 8 20 293.9 us 3.39 us 2.83 us 294.2 us 289.6 us 298.2 us 21.0280 - 89.89 KB
Get 8 40 623.9 us 7.64 us 6.38 us 622.0 us 616.6 us 636.7 us 40.1003 5.0125 178.67 KB
Get 16 10 352.7 us 19.18 us 21.32 us 342.2 us 334.8 us 403.9 us 21.8281 1.3643 93.12 KB
Get 16 20 779.2 us 49.66 us 51.00 us 762.8 us 717.7 us 910.1 us 41.4747 4.6083 181.9 KB
Get 16 40 1,591.5 us 49.01 us 54.47 us 1,574.0 us 1,504.9 us 1,729.6 us 80.2469 18.5185 363.09 KB
Get 32 10 836.8 us 18.17 us 20.20 us 828.8 us 818.1 us 884.3 us 42.1053 7.0175 188.65 KB
Get 32 20 2,023.2 us 249.90 us 287.79 us 1,848.4 us 1,772.2 us 2,564.3 us 86.3309 - 369.84 KB
Get 32 40 4,059.6 us 310.17 us 344.75 us 3,896.3 us 3,715.2 us 4,839.4 us 155.1724 51.7241 738.26 KB

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Aug 29, 2022
@ghost
Copy link

ghost commented Aug 29, 2022

Tagging subscribers to this area: @dotnet/area-extensions-configuration
See info in area-owners.md if you want to be subscribed.

Issue Details

@rjgotten I implemented your design at #65885

The benchmark of dotnet/performance#2572 is improved to

Method MySettingsCount Mean Error StdDev Median Min Max Gen 0 Gen 1 Allocated
Get 32 71.51 us 2.089 us 2.236 us 71.06 us 68.39 us 76.94 us 7.6087 - 32.95 KB
Get 64 189.85 us 6.602 us 7.338 us 190.66 us 178.27 us 207.54 us 15.1188 0.7199 64.27 KB
Get 128 569.81 us 11.086 us 10.888 us 572.06 us 550.44 us 590.68 us 29.0828 2.2371 128.01 KB
Author: wangyuantao
Assignees: -
Labels:

area-Extensions-Configuration

Milestone: -

@wangyuantao wangyuantao marked this pull request as ready for review August 29, 2022 04:46
@wangyuantao wangyuantao changed the title sort out side of GetChildKeys Sort out side of GetChildKeys Aug 29, 2022
@eerhardt eerhardt requested a review from halter73 August 30, 2022 23:14
@wangyuantao wangyuantao changed the title Sort out side of GetChildKeys Optimize the performance of InternalConfigurationRootExtensions.GetChildrenImplementation Sep 1, 2022
@wangyuantao wangyuantao requested review from eerhardt and removed request for halter73 September 1, 2022 23:51
@wangyuantao wangyuantao requested review from halter73 and removed request for eerhardt September 4, 2022 00:47
@rjgotten
Copy link

rjgotten commented Sep 5, 2022

allocation difference following an ~8:1 before:after ratio
gen1 garbage collection ~5:1
gen0 garbage collection ~50:1

Wow. Just. Wow

@wangyuantao
Copy link
Author

allocation difference following an ~8:1 before:after ratio
gen1 garbage collection ~5:1
gen0 garbage collection ~50:1

Wow. Just. Wow

The challenging part is how to make the back compat perfect. Actually I'm not very confident about current change, because the behavior of the GetChildren changed - previously the final keys order is determined by the last provider, now with my change, the keys order is determined by the us internally which is always sorted. If some clients hack the provider and use the last provider to output the keys in a specific order, now they cannot depend on that specific order...

@@ -24,11 +24,15 @@ internal static IEnumerable<IConfigurationSection> GetChildrenImplementation(thi
IEnumerable<IConfigurationProvider> providers = reference?.Providers ?? root.Providers;

IEnumerable<IConfigurationSection> children = providers
#if NET7_0_OR_GREATER
.SelectMany(p => p.GetChildKeys(path))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tantamount to the following unless we take advantage of the new GetChildKeys interface method in our ConfigurationProvider and ChainedConfigurationProvider classes to stop sorting since that would be non-breaking.

Suggested change
.SelectMany(p => p.GetChildKeys(path))
.SelectMany(p => p.GetChildKeys(Enumerable.Empty<string>(), path))

If the problem is just the bad asymptotic cost of resorting previous keys for each additional provider, and not sorting keys internally, I don't think we need the new interface.

However, we then still need to sort all the keys at the end of the GetChildrenImplementation to avoid breaking callers that expect sorted keys from default IConfiguration.GetChildren() implementations.

The challenging part is how to make the back compat perfect. Actually I'm not very confident about current change, because the behavior of the GetChildren changed - previously the final keys order is determined by the last provider, now with my change, the keys order is determined by the us internally which is always sorted. If some clients hack the provider and use the last provider to output the keys in a specific order, now they cannot depend on that specific order...

I'm not sure we need to be worried about developers using the last provider to hijack the earlierKeys from earlier providers. Does anyone know of anything that does this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another idea for back compat is to let caller decide the implementation. For commonly used caller like Binder, we know the optimized path can be used for sure. How do you think? Send a new iteration for brainstorming.

wangyuantao and others added 2 commits September 6, 2022 22:12
…onfigurationRootExtensions.cs


Agree. No need to introduce new interface.

Co-authored-by: Stephen Halter <[email protected]>
@tarekgh
Copy link
Member

tarekgh commented Sep 27, 2022

We are closing this since we haven't decided about the best way to handle this optimization. We can still discuss more as needed for how we can do it.

@tarekgh tarekgh closed this Sep 27, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Oct 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Extensions-Configuration community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants